package com.cifer.controller;

import com.cifer.pojo.Param;
import com.cifer.pojo.Result;
import com.cifer.pojo.SaveParam;
import com.cifer.pojo.SettingParam;
import com.cifer.utils.FileHandler;
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.cifer.utils.TimeUtils.timeInterVal;
import static java.lang.Math.abs;
import static java.lang.Math.pow;

@RestController
@Slf4j
public class AnalyzeController {
    //要处理列的下标
    static final int INDEX_OF_TIME = 2;
    static final int INDEX_OF_VELOCITY = 6;
    static final int INDEX_OF_ANGLE = 9;
    //配置参数，n,m,p,q,r,compensationV

    private Double compensationV = 0.0;
    private Double n = 1.0;
    private Double m = 1.0;
    private Double p = 1.0;
    private Double q = 1.0;
    private Double r = 1.0;
    //配置腿leg、脚foot数据文件名,方便之后根据文件名判读是腿数据还是脚数据
    //可被修改，用来告诉程序，读取到的最新文件夹下，哪个是腿数据的文件名
    private String legDataFileName = "COM11";
    private String footDataFileName = "COM10";

    @CrossOrigin
    @GetMapping("/analyze")
    //前端可能会传统日期、时间(代表选择指定文件),开始，结束数字点(代表要截取计算的位置)

    public Result getParam(String date, String time, String start, String end) throws IOException, CsvException {
        //对传入的开始、结束索引点进行判断，有一空串、一不空串返回起、始索引不全
        if ((start.length() != 0 && end.length() == 0) || (start.length() == 0 && end.length() != 0)) {
            return Result.error("起、始索引不全");
        }
        //
        //接收两个参数，如果其中一个为空串是有问题的得返回个错误结果
        if ((date.length() != 0 && time.length() == 0) || (date.length() == 0 && time.length() != 0)) {
            return Result.error("文件信息不全");
        }
        File targetFile;
        //如果两个参数都不为空串，说明要获取指定文件来进行处理
        if (date.length() != 0 && time.length() != 0) {
            targetFile = FileHandler.getFile(date, time);
        } else {
            //其他一般情况，两个字符串均为空串
            //获取最新数据文件
            targetFile = FileHandler.getLatestFile();
        }


        //将最新文件下的两个数据文件分配
        File footFile;
        File legFile;
        //此时latestFile对应最新的时间文件
        File[] datas = targetFile.listFiles();
        if (datas[0].getName().contains(footDataFileName)&&datas[1].getName().contains(legDataFileName)) {
            footFile = datas[0];
            legFile = datas[1];
        } else if(datas[0].getName().contains(legDataFileName)&&datas[1].getName().contains(footDataFileName)){
            footFile = datas[1];
            legFile = datas[0];
        }
        else{
            return Result.error("COM口不匹配,请检查一下!!");
        }

        CSVReader footDataReader = new CSVReader(new FileReader(footFile));
        CSVReader legDataReader = new CSVReader(new FileReader(legFile));
        //后面的带10的默认是脚的数据，带11的默认是腿的数据
        List<String[]> data10 = footDataReader.readAll();
        List<String[]> data11 = legDataReader.readAll();
        footDataReader.close();
        legDataReader.close();

        int length=data10.size()-20;
        if(data10.size()>data11.size()){
            length=data11.size()-20;
        }


        //定义装腿脚角度差、腿角度、脚角度、时间序列容器
        ArrayList<Double> angle = new ArrayList<>();
        ArrayList<Double> legAngle = new ArrayList<>();
        ArrayList<Double> footAngle = new ArrayList<>();
        ArrayList<Double> timeSequence=new ArrayList<>();

        //用于找最大值和最大值所对应的下标
        int maxId = -1;
        double maxAngle = -1000;
//        //用于找最小值和最小值所对应的下标
        int minId = -1;
        double minAngle = 1000;
//        //用于找角速度最小值
        double minVelocity = 1000;
        //确定截取位置,默认为1到length
        int startIndex = 1, endIndex = length;

        //如果start、end不空，且解析成功，则startIndex、endIndex用解析后的结果
        if (start.length() != 0 && end.length() != 0) {

            try {
//                startIndex = Integer.parseInt(start);
//                endIndex = Integer.parseInt(end);
                startIndex = Integer.parseInt(start)/2+1;
                endIndex = Integer.parseInt(end)/2+1;
//                startIndex=(int)(Double.parseDouble(start)/5.0+1);
//                endIndex=(int)(Double.parseDouble(end)/5.0+1);
            } catch (NumberFormatException e) {
                e.printStackTrace();
                return Result.error("非法索引,请重新输入!");
            }
            if (startIndex < 1 || endIndex > length || startIndex > endIndex) {
                return Result.error("索引越界或开始索引大于结束索引");
            }
        }
        //拿到起始的时间字符串
        String startTime=data10.get(startIndex)[INDEX_OF_TIME];

        //如果腿数据的接口为空字符串，则要特殊处理一下,只用脚的数据
        if (legDataFileName.length() == 0) {

            for (int i = startIndex; i <= endIndex; i++) {
                String[] row10 = data10.get(i);
                String agl10 = row10[INDEX_OF_ANGLE];
                double footA = Double.parseDouble(agl10);
                double angleV = 0 - footA + compensationV;
                if (angleV > maxAngle) {
                    maxAngle = angleV;
                    maxId = i;
                }
                if (angleV < minAngle) {
                    minAngle = angleV;
                    minId = i;
                }
                angle.add(angleV);
                legAngle.add(0.0);
                footAngle.add(footA);
                //对角速度进行处理
                String velocity10 = row10[INDEX_OF_VELOCITY];
                double velocityV = 0 - Double.parseDouble(velocity10);
                if (velocityV < minVelocity) {
                    minVelocity = velocityV;
                }
                //收集时间序列,这里收集毫秒值
                Double t=timeInterVal(startTime,row10[INDEX_OF_TIME])*1000;
                timeSequence.add(t);
//                timeSequence.add((i-1)*5.0);


            }

        }
        //如过两COM接口均正常，则执行正常的流程
        else {
            for (int i = startIndex; i <=endIndex; i++) {
                String[] row11 = data11.get(i);
                String[] row10 = data10.get(i);
                //获取到两个原角度,转换成double后相减，值存入angel集合中
                String agl11 = row11[INDEX_OF_ANGLE];
                String agl10 = row10[INDEX_OF_ANGLE];
                double legA = Double.parseDouble(agl11);
                double footA = Double.parseDouble(agl10);
                //加上补偿值
                double angleV = legA - footA + compensationV;

                if (angleV > maxAngle) {
                    maxAngle = angleV;
                    maxId = i;
                }
                if (angleV < minAngle) {
                    minAngle = angleV;
                    minId = i;
                }
                angle.add(angleV);
                legAngle.add(legA);
                footAngle.add(footA);
//
//            //对角速度进行处理
                String velocity11 = row11[INDEX_OF_VELOCITY];
                String velocity10 = row10[INDEX_OF_VELOCITY];
                double velocityV = Double.parseDouble(velocity11) - Double.parseDouble(velocity10);
                if (velocityV < minVelocity) {
                    minVelocity = velocityV;
                }

                //收集时间序列
                Double t=timeInterVal(startTime,row10[INDEX_OF_TIME])*1000;
                timeSequence.add(t);
//                timeSequence.add((i-1)*5.0);

            }
        }

        //找最大角度及对应时间成功
        log.info("触地角度:{}", maxAngle);

        //找最小角度及对应时间成功
        log.info("最大后足角度:{}", minAngle);

        Double maxDiff = maxAngle - minAngle;
        Double maxDiffInterval = abs(timeInterVal(data10.get(maxId)[INDEX_OF_TIME], data10.get(minId)[INDEX_OF_TIME]));
        //最大和最小角度的时间差不用源文件的时间计算了，改用每行0.005s计算
//        Double maxDiffInterval=abs(maxId-minId)*0.005;
        log.info("总后足运动:{}", maxDiff);
        log.info("时间差{}", maxDiffInterval);
//
//        //测试找最小角速度
        log.info("峰值角速度:{}", minVelocity);
        //求稳定性指标
        Double stabilityIndex = pow(maxDiffInterval, n) / pow(maxAngle, m) / pow(minAngle, p) / pow(maxDiff, q) / pow(minVelocity, r);
        log.info("稳定性指标:{}", stabilityIndex);
        log.info("角度数据:{}", angle);
        log.info("时间序列数据{}",timeSequence);
        Param param = new Param(maxAngle, minAngle, maxDiff, maxDiffInterval, minVelocity, stabilityIndex, angle, legAngle, footAngle,timeSequence);
        return Result.success(param);
    }

    //如果时间、日期不指定，则将最新文件复制到指定文件夹，如有指定，则将指定文件复制到指定文件方法
    @CrossOrigin
    @PostMapping("/copy")
    public Result copyFileTo(@RequestBody SaveParam sp) throws IOException {
        String path = sp.getPath();
        String date = sp.getDate();
        String time = sp.getTime();
        if (path.length() == 0) {
            return Result.error("请先输入保存路径");
        }
        //如果不是文件格式，给前端响应一下
        if (!FileHandler.isFilePath(path)) {
            return Result.error("路径格式有误，请检查一下");
        }
        //前端不会传入一个为空字符串，一个不是空字符串的情况
//        //如果date不为空串，time为空串，给前端响应提示一下
//        if(date.length()!=0&&time.length()==0){
//            return Result.error("时间");
//        }
        //将待保存值全部拿出
        int count=sp.getCount();
        Double maxAngle = sp.getMaxAngle();
        Double minAngle = sp.getMinAngle();
        Double maxDiff = sp.getMaxDiff();
        Double maxDiffInterval = sp.getMaxDiffInterval();
        Double minVelocity = sp.getMinVelocity();
        Double stabilityIndex = sp.getStabilityIndex();
        ArrayList<Double> angle = sp.getAngle();
        //如果date,time均不为空串，选用指定文件否则，指定最新文件
        File targetFile;
        if (date.length() != 0 && time.length() != 0) {
            targetFile = FileHandler.getFile(date, time);
        } else {
            targetFile = FileHandler.getLatestFile();
        }

        File destinationFile = new File(path, targetFile.getName());
        //后续如果mkdirs失败了，说明target文件已存在
        if (!destinationFile.mkdirs()) {
            return Result.error("目标文件已存在!!");
        }
        File legCopy = new File(destinationFile, "leg-"+count+".csv");
        File footCopy = new File(destinationFile, "foot-"+count+".csv");
        File paramFile = new File(destinationFile, "paramData-"+count+".csv");
        //分别获取leg、foot的file数据
        File[] files = targetFile.listFiles();
        File legfile;
        File footfile;
        if (files[0].getName().contains(legDataFileName)) {
            legfile = files[0];
            footfile = files[1];
        } else {
            legfile = files[1];
            footfile = files[0];
        }

        FileHandler.fileCopy(legfile, legCopy);
        FileHandler.fileCopy(footfile, footCopy);
        //保存参数逻辑处理
        //开启针对param的文件输出流
        FileOutputStream fos = new FileOutputStream(paramFile);
        StringBuffer sb = new StringBuffer();
        sb.append("TA(°),").append(maxAngle).append("\n");
        sb.append("MRA(°),").append(minAngle).append("\n");
        sb.append("TRM(°),").append(maxDiff).append("\n");
        sb.append("TMRA(s),").append(maxDiffInterval).append("\n");
        sb.append("Vpeak(°/s),").append(minVelocity).append("\n");
        sb.append("W,").append(stabilityIndex).append("\n");
        sb.append("angle,");
        for(int i=0;i<angle.size();i++){
            sb.append(angle.get(i));
            if(i!=angle.size()-1){
                sb.append(",");
            }
        }
        sb.append("\n");
        fos.write(sb.toString().getBytes(StandardCharsets.UTF_8));
        fos.close();
        return Result.success();


    }
    //获取配置参数的方法
    @CrossOrigin
    @GetMapping("/get-param")
    public Result getSettingParam(){
        SettingParam settingParam = new SettingParam(this.compensationV, this.n, this.m, this.p, this.q, this.r, this.legDataFileName, this.footDataFileName);
        return Result.success(settingParam);
    }

    //修改配置参数方法
    @CrossOrigin
    @GetMapping("/param")
    public Result modifySettingParam(SettingParam sp) {
        //检查用户对某些参数是否传递了空参数(特别地增加了脚foot数据不能为空的判断，腿leg为空，代表其他特殊处理;10.6:这个判断前端已经处理好了，不会传递有问题的数据过来，不过留着吧
        if (sp.getR() == null || sp.getQ() == null || sp.getP() == null || sp.getN() == null || sp.getM() == null || sp.getCompensationV() == null || sp.getFootDataFileName() == "") {
            return Result.error("(除腿COM口外)参数设置不能为空");
        }
        this.n = sp.getN();
        this.m = sp.getM();
        this.p = sp.getP();
        this.q = sp.getQ();
        this.r = sp.getR();
        this.compensationV = sp.getCompensationV();
        this.legDataFileName = sp.getLegDataFileName();
        this.footDataFileName = sp.getFootDataFileName();
        return Result.success();
    }

    //获取Record目录下所有的日期文件，及对应的子文件数组的键值对map

    @CrossOrigin
    @GetMapping("/dirs")
    public Result getDirectoryMap() {
        Map<String, List> directoryMap = FileHandler.getDirectoryMap();
        return Result.success(directoryMap);

    }

}
